iT邦幫忙

2026 iThome 鐵人賽

0
AI Engineering

Java × AI:30天打造智慧健康助理系列 第 8 篇

讓 Spring Boot 回傳 JSON

  • 分享至 

  • xImage
  •  

Day 8|讓 Spring Boot 回傳 JSON

昨天的 Day 7,我們終於完成了智慧健康助理的第一個健康功能:BMI 計算器!

只要在瀏覽器輸入:

http://localhost:8080/bmi?height=162&weight=50

Spring Boot 就會根據身高與體重計算 BMI,並回傳:

Your BMI is: 19.05

雖然功能已經可以正常運作,但我開始思考一個問題:

如果之後想讓前端網頁顯示身高、體重和 BMI,難道只能從這段文字裡面找出數字嗎?

這樣好像不太方便。

所以今天決定讓 BMI API 升級,從原本回傳一段文字,改成回傳 JSON 格式的資料。

今天的目標是:

認識 JSON,並讓 Spring Boot 回傳包含身高、體重與 BMI 的結構化資料。


一、JSON 到底是什麼?

JSON 的全名是:

JavaScript Object Notation

它是一種常見的資料交換格式。

雖然名字裡面有 JavaScript,但 JSON 並不是只有 JavaScript 才能使用。

Java、Python 等程式語言也都可以處理 JSON。

JSON 最大的特色,就是可以使用比較有結構的方式來表示資料。

例如昨天的 BMI 結果:

Your BMI is: 19.05

如果改成 JSON,就可以寫成:

{
  "height": 162,
  "weight": 50,
  "bmi": 19.05
}

這樣就能清楚知道:

  • height 代表身高
  • weight 代表體重
  • bmi 代表計算結果

比起只有一句文字,JSON 更方便程式讀取與處理。


二、JSON 的基本結構

JSON 主要使用 Key-Value(鍵值對) 來表示資料。

例如:

{
  "name": "H",
  "age": 20,
  "height": 162
}

其中:

"name"

就是 Key。

而:

"H"

就是 Value。

可以把它理解成:

Key → Value

name → H
age → 20
height → 162

JSON 可以儲存不同類型的資料,例如:

資料類型 範例
String "H"
Number 162
Boolean true
Array [6, 7, 8]
Object {"bmi": 19.05}
Null null

例如未來的健康助理可能會使用:

{
  "name": "H",
  "height": 162,
  "weight": 50,
  "sleepHours": [6, 7, 8],
  "exerciseToday": true
}

這樣就能把不同的健康資料整理在同一個物件裡。


三、回顧昨天的 BMI API

昨天的程式是:

@GetMapping("/bmi")
public String calculateBMI(
        @RequestParam double height,
        @RequestParam double weight) {

    if (height <= 0 || weight <= 0) {
        return "Height and weight must be greater than 0.";
    }

    double heightInMeters = height / 100;

    double bmi = weight / (heightInMeters * heightInMeters);

    return "Your BMI is: " + String.format("%.2f", bmi);
}

這個 Method 的回傳型別是:

String

因此最後回傳的是一段文字。

但今天希望回傳:

{
  "height": 162,
  "weight": 50,
  "bmi": 19.05
}

所以我們需要修改回傳資料的方式。


四、認識 Map

在 Java 裡面,可以使用 Map 來儲存 Key-Value 資料。

例如:

Map<String, Object> result = new HashMap<>();

這裡出現兩個新東西:

Map

Map 是 Java 用來儲存鍵值對資料的介面。

HashMap

HashMap 是 Map 的其中一種實作方式。

接下來可以利用:

result.put("height", 162);
result.put("weight", 50);
result.put("bmi", 19.05);

把資料放進 Map。

如果用表格表示:

Key Value
height 162
weight 50
bmi 19.05

而 Spring Boot 在具備 JSON 序列化功能的情況下,可以將這個 Map 自動轉換成 JSON 回傳。

這樣就不需要自己手動拼接 JSON 字串。


五、修改 BMI API

接下來正式修改昨天的 BMI API。

這次建立一個新的 BmiController.java,並將原本的 /bmi 方法移到這裡,避免兩個 Controller 同時使用相同路徑。

完整程式如下:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
public class BmiController {

    @GetMapping("/bmi")
    public Map<String, Object> calculateBMI(
            @RequestParam double height,
            @RequestParam double weight) {

        Map<String, Object> result = new HashMap<>();

        if (!Double.isFinite(height) ||
            !Double.isFinite(weight) ||
            height <= 0 || weight <= 0) {

            result.put("error",
                    "Height and weight must be valid positive numbers.");

            return result;
        }

        double heightInMeters = height / 100;

        double bmi = weight / (heightInMeters * heightInMeters);

        if (!Double.isFinite(bmi)) {
            result.put("error", "Unable to calculate BMI.");
            return result;
        }

        double roundedBmi = Math.round(bmi * 100.0) / 100.0;

        result.put("height", height);
        result.put("weight", weight);
        result.put("bmi", roundedBmi);

        return result;
    }
}

這次的程式跟昨天相比,有幾個重要的改變。


六、第一個改變:回傳型別

昨天使用:

public String calculateBMI(...)

今天改成:

public Map<String, Object> calculateBMI(...)

這代表現在 Method 回傳的不再是一段 String,而是一個 Map。

而:

Map<String, Object>

代表:

  • Key 使用 String
  • Value 可以存放不同類型的物件

所以可以同時儲存:

result.put("height", height);
result.put("weight", weight);
result.put("bmi", roundedBmi);

不需要將所有數字都轉換成文字。


七、第二個改變:使用 Math.round()

昨天使用:

String.format("%.2f", bmi)

將 BMI 顯示到小數點後兩位。

但這次希望 JSON 裡面的 BMI 保持 Number 類型,而不是 String。

因此改成:

double roundedBmi = Math.round(bmi * 100.0) / 100.0;

例如:

原始 BMI = 19.05197378448407

先乘以 100:

1905.197378448407

再四捨五入:

1905

最後除以 100:

19.05

這樣就能得到:

{
  "bmi": 19.05
}

而不是:

{
  "bmi": "19.05"
}

兩者看起來很像,但其實資料類型不同。

前者是 Number,後者是 String。


八、實際測試 BMI API

接下來重新啟動 Spring Boot。

在瀏覽器輸入:

http://localhost:8080/bmi?height=162&weight=50

預期會得到:

{
  "weight": 50.0,
  "height": 162.0,
  "bmi": 19.05
}

因為使用的是 HashMap,所以 JSON 欄位的順序不一定相同。

但只要資料內容正確,就代表成功了!

現在我們的 API 已經可以回傳真正的 JSON 資料。


九、如果輸入錯誤資料呢?

昨天有加入簡單的錯誤判斷。

今天也保留這個功能。

例如:

http://localhost:8080/bmi?height=0&weight=50

因為身高不能是 0,所以程式會進入:

if (!Double.isFinite(height) ||
    !Double.isFinite(weight) ||
    height <= 0 || weight <= 0) {

    result.put("error",
            "Height and weight must be valid positive numbers.");

    return result;
}

最後回傳:

{
  "error": "Height and weight must be valid positive numbers."
}

這樣錯誤訊息也可以用 JSON 表示。

不過目前這個版本仍然是簡化的錯誤處理。

例如使用者輸入非數字,Spring Boot 通常會回傳 HTTP 400 錯誤,而不是進入我們自己寫的判斷。

未來還可以進一步學習如何統一處理這些錯誤。


十、Spring Boot 為什麼可以自動轉換 JSON?

做到這裡,我開始好奇:

明明 Java 回傳的是 Map,為什麼瀏覽器收到的會是 JSON?

這其實和 Spring Boot 的 JSON 序列化機制有關。

當我們使用:

@RestController

並回傳:

Map<String, Object>

Spring MVC 會透過合適的 HttpMessageConverter,將 Java 物件轉換成 HTTP Response 的內容。

在具備 Jackson 等 JSON 處理套件的常見 Spring Web 設定下,就可以自動完成 JSON 序列化。

整個流程可以理解成:

使用者發送 Request
        ↓
Spring Boot
        ↓
BmiController
        ↓
Java 計算 BMI
        ↓
將結果放入 Map
        ↓
JSON 序列化
        ↓
HTTP Response
        ↓
使用者收到 JSON

這也是 Spring Boot 很方便的地方。

我們不需要每次都自己把 Java 資料手動轉成 JSON 字串。


十一、為什麼 JSON 對智慧健康助理很重要?

現在可能會覺得:

「不就是把一段文字改成 JSON 嗎?」

但其實這一步對後面的專案很重要。

因為未來的智慧健康助理不會只有 BMI。

還可能包含:

  • 每日睡眠時間
  • 飲水量
  • 運動紀錄
  • 體重變化
  • AI 產生的健康資訊摘要

例如:

{
  "name": "H",
  "healthData": {
    "height": 162,
    "weight": 50,
    "bmi": 19.05,
    "sleepHours": 7,
    "waterIntake": 1500
  }
}

未來如果想把資料傳給前端網頁,或是交給其他服務處理,JSON 就可以作為資料交換的格式。

甚至在之後串接生成式 AI 時,也可能會使用 JSON 組織 API Request 與 Response。

所以今天雖然沒有加入新的健康功能,但其實是在替之後的資料交換打基礎。


十二、Day 7 和 Day 8 有什麼差別?

整理一下這兩天的變化。

Day 7

回傳一段文字:

Your BMI is: 19.05

優點是簡單、容易理解。

但如果其他程式想取得 BMI 數字,就比較不方便。

Day 8

改成回傳 JSON:

{
  "height": 162.0,
  "weight": 50.0,
  "bmi": 19.05
}

這樣資料就有明確的欄位名稱,也比較方便後續處理。

比較項目 Day 7 Day 8
回傳型別 String Map
回傳內容 一段文字 JSON
資料結構 無明確欄位 Key-Value
前端讀取 需要額外解析文字 可依欄位取得資料
後續擴充 較不方便 較方便

這也讓我更了解,為什麼實際開發 API 時經常會看到 JSON。


Day 8 小結

今天把昨天的 BMI API 升級成 JSON 版本。

也學到了幾個新概念:

  1. JSON:常見的資料交換格式
  2. Key-Value:使用鍵值對表示資料
  3. Map:Java 用來儲存鍵值對資料的介面
  4. HashMap:Map 的其中一種實作
  5. JSON 序列化:將 Java 物件轉換成 JSON 格式

現在智慧健康助理已經可以:

接收身高與體重
       ↓
Java 計算 BMI
       ↓
將結果放入 Map
       ↓
Spring Boot 轉換成 JSON
       ↓
回傳給使用者

比起昨天只能回傳一句話,今天的 API 終於開始有比較完整的資料結構了!

雖然目前還沒有真正串接 AI,但我覺得這些基礎都很重要。

畢竟之後如果要讓 Java 和 AI 交換資料,就必須先了解資料應該怎麼組織與傳遞。


明日預告

Day 9|不只用網址傳資料!認識 POST 與 @RequestBody

目前我們都是透過網址傳送資料:

/bmi?height=162&weight=50

但是如果未來要傳送更多健康資訊,例如:

{
  "name": "H",
  "height": 162,
  "weight": 50,
  "sleepHours": 7,
  "waterIntake": 1500
}

全部放在網址裡就不太方便了。

所以下一篇準備認識:

  • HTTP POST
  • @PostMapping
  • @RequestBody
  • 如何讓 Spring Boot 接收 JSON

讓智慧健康助理開始學會接收更完整的健康資料!


上一篇
第一個健康功能!
下一篇
讓 Spring Boot 接收 JSON
系列文
Java × AI:30天打造智慧健康助理 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言